﻿
Public Class DataTypeMapper

    Public Function MapFromDBType(serverType__1 As ServerType, dataType As String, dataLength As System.Nullable(Of Integer), dataPrecision As System.Nullable(Of Integer), dataScale As System.Nullable(Of Integer)) As Type
        Select Case serverType__1
            Case ServerType.SqlServer
                Return MapFromSqlServerDBType(dataType, dataLength, dataPrecision, dataScale)
            Case ServerType.Oracle
                Return MapFromOracleDBType(dataType, dataLength, dataPrecision, dataScale)
            Case ServerType.MySQL
                Return MapFromMySqlDBType(dataType, dataLength, dataPrecision, dataScale)
            Case ServerType.PostgreSQL
                Return MapFromPostgreDBType(dataType, dataLength, dataPrecision, dataScale)
        End Select
        Return MapFromDBType(dataType, dataLength, dataPrecision, dataScale)
    End Function

    'http://msdn.microsoft.com/en-us/library/cc716729.aspx
    Private Function MapFromSqlServerDBType(dataType As String, dataLength As System.Nullable(Of Integer), dataPrecision As System.Nullable(Of Integer), dataScale As System.Nullable(Of Integer)) As Type
        Return MapFromDBType(dataType, dataLength, dataPrecision, dataScale)
    End Function

    'http://docs.oracle.com/cd/B28359_01/win.111/b28376/appendixa.htm
    Private Function MapFromOracleDBType(dataType As String, dataLength As System.Nullable(Of Integer), dataPrecision As System.Nullable(Of Integer), dataScale As System.Nullable(Of Integer)) As Type
        If String.Equals(dataType, "DATE", StringComparison.OrdinalIgnoreCase) OrElse String.Equals(dataType, "TIMESTAMP", StringComparison.OrdinalIgnoreCase) OrElse String.Equals(dataType, "TIMESTAMP WITH TIME ZONE", StringComparison.OrdinalIgnoreCase) OrElse String.Equals(dataType, "TIMESTAMP WITH LOCAL TIME ZONE", StringComparison.OrdinalIgnoreCase) Then
            Return GetType(System.DateTime)
        End If

        If String.Equals(dataType, "NUMBER", StringComparison.OrdinalIgnoreCase) Then
            If dataScale.GetValueOrDefault(0) = 0 Then
                'Integer type
                If dataPrecision.GetValueOrDefault(0) >= 1 AndAlso dataPrecision.GetValueOrDefault(0) <= 4 Then
                    Return GetType(System.Int16)
                End If
                If dataPrecision.GetValueOrDefault(0) >= 5 AndAlso dataPrecision.GetValueOrDefault(0) <= 9 Then
                    Return GetType(System.Int32)
                End If
                If dataPrecision.GetValueOrDefault(0) >= 10 AndAlso dataPrecision.GetValueOrDefault(0) <= 18 Then
                    Return GetType(System.Int64)
                End If
            End If
            If dataScale.GetValueOrDefault(0) > 0 Then
                'Floating type
                If dataPrecision.GetValueOrDefault(0) >= 1 AndAlso dataPrecision.GetValueOrDefault(0) <= 7 Then
                    Return GetType(System.Single)
                End If
                If dataPrecision.GetValueOrDefault(0) >= 8 AndAlso dataPrecision.GetValueOrDefault(0) <= 15 Then
                    Return GetType(System.Double)
                End If
            End If
            Return GetType(System.Decimal)
        End If

        If String.Equals(dataType, "BLOB", StringComparison.OrdinalIgnoreCase) OrElse String.Equals(dataType, "BFILE *", StringComparison.OrdinalIgnoreCase) OrElse String.Equals(dataType, "LONG RAW", StringComparison.OrdinalIgnoreCase) OrElse String.Equals(dataType, "RAW", StringComparison.OrdinalIgnoreCase) Then
            Return GetType(Byte())
        End If

        If String.Equals(dataType, "INTERVAL DAY TO SECOND", StringComparison.OrdinalIgnoreCase) Then
            Return GetType(System.TimeSpan)
        End If

        If String.Equals(dataType, "INTERVAL YEAR TO MONTH", StringComparison.OrdinalIgnoreCase) OrElse String.Equals(dataType, "LONG", StringComparison.OrdinalIgnoreCase) Then
            Return GetType(System.Int64)
        End If

        If String.Equals(dataType, "BINARY_FLOAT", StringComparison.OrdinalIgnoreCase) Then
            Return GetType(System.Single)
        End If

        If String.Equals(dataType, "BINARY_DOUBLE", StringComparison.OrdinalIgnoreCase) Then
            Return GetType(System.Double)
        End If

        If String.Equals(dataType, "BINARY_INTEGER", StringComparison.OrdinalIgnoreCase) OrElse String.Equals(dataType, "FLOAT", StringComparison.OrdinalIgnoreCase) OrElse String.Equals(dataType, "REAL", StringComparison.OrdinalIgnoreCase) Then
            Return GetType(System.Decimal)
        End If

        Return GetType(System.String)
    End Function

    Private Function MapFromMySqlDBType(dataType As String, dataLength As System.Nullable(Of Integer), dataPrecision As System.Nullable(Of Integer), dataScale As System.Nullable(Of Integer)) As Type
        Return MapFromDBType(dataType, dataLength, dataPrecision, dataScale)
    End Function

    Private Function MapFromPostgreDBType(dataType As String, dataLength As System.Nullable(Of Integer), dataPrecision As System.Nullable(Of Integer), dataScale As System.Nullable(Of Integer)) As Type
        Return MapFromDBType(dataType, dataLength, dataPrecision, dataScale)
    End Function

    Private Function MapFromDBType(dataType As String, dataLength As System.Nullable(Of Integer), dataPrecision As System.Nullable(Of Integer), dataScale As System.Nullable(Of Integer)) As Type
        If dataType = "DATE" OrElse dataType = "date" OrElse dataType = "datetime" OrElse dataType = "datetime2" OrElse dataType = "TIMESTAMP" OrElse dataType = "TIMESTAMP WITH TIME ZONE" OrElse dataType = "TIMESTAMP WITH LOCAL TIME ZONE" OrElse dataType = "smalldatetime" Then
            Return GetType(DateTime)
        End If
        If dataType = "NUMBER" OrElse dataType = "LONG" OrElse dataType = "bigint" Then
            Return GetType(Long)
        End If
        If dataType = "smallint" Then
            Return GetType(Int16)
        End If
        If dataType = "tinyint" Then
            Return GetType([Byte])
        End If
        If dataType = "int" OrElse dataType = "INTERVAL YEAR TO MONTH" OrElse dataType = "BINARY_INTEGER" OrElse dataType.Contains("int") Then
            Return GetType(Integer)
        End If
        If dataType = "BINARY_DOUBLE" OrElse dataType = "float" OrElse dataType = "numeric" Then
            Return GetType(Double)
        End If
        If dataType = "BINARY_FLOAT" OrElse dataType = "FLOAT" Then
            Return GetType(Single)
        End If
        If dataType = "BLOB" OrElse dataType = "BFILE *" OrElse dataType = "LONG RAW" OrElse dataType = "binary" OrElse dataType = "image" OrElse dataType = "timestamp" OrElse dataType = "varbinary" Then
            Return GetType(Byte())
        End If
        If dataType = "INTERVAL DAY TO SECOND" Then
            Return GetType(TimeSpan)
        End If
        If dataType = "bit" Then
            Return GetType([Boolean])
        End If
        If dataType = "decimal" OrElse dataType = "money" OrElse dataType = "smallmoney" OrElse dataType = "numeric" Then
            Return GetType(Decimal)
        End If
        If dataType = "real" Then
            Return GetType([Single])
        End If
        If dataType = "uniqueidentifier" Then
            Return GetType(Guid)
        End If

        ' CHAR, CLOB, NCLOB, NCHAR, XMLType, VARCHAR2, nchar, ntext
        Return GetType(String)
    End Function

End Class

